#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define RET_SUCESS  0
#define RET_FAILED  1

#define CACHE_LINE_SIZE  64   
#define THREAD_MAX_NUM   256

struct _g_data_ {
    long top_half_data;
    long pad[CACHE_LINE_SIZE/sizeof(long)];
    long bottom_half_data;
} g_data;

int g_state;

//this can be change to other lock mechanism.
pthread_mutex_t g_mutex;
pthread_t threads[256];


void *thread_function(void *arg)
{
    int data = (int)arg;

    //stage 0, without protection
    while(g_state == 0) {
        g_data.top_half_data = data;
        g_data.bottom_half_data = data;
    }
    
    //stage 1, with protection
    while(g_state == 1) {
        pthread_mutex_lock(&g_mutex);
        g_data.top_half_data = data;
        g_data.bottom_half_data = data;
        pthread_mutex_unlock(&g_mutex);
    }

    return NULL;
}

int main(void)
{
    int result = RET_SUCESS;    
    int cpu_cnt;
    int i,times;

    cpu_cnt = (int)sysconf(_SC_NPROCESSORS_ONLN);
    if(cpu_cnt <= 0) {
        printf("get cup num failed!!\n");
        return -1;
    }
    if(cpu_cnt > THREAD_MAX_NUM) {
        cpu_cnt = THREAD_MAX_NUM;
    }
    
    g_state = 0;
    g_data.top_half_data = g_data.bottom_half_data = 0;
    
    //create threads
    for (i = 0; i < cpu_cnt - 1; i++){
        pthread_create(&threads[i], NULL, thread_function,(void *)i); 
    }

    while(1) {
        if(g_data.top_half_data != g_data.bottom_half_data) {
            printf("data error without mutex lock\n");
            break;
        }
    }
    
    g_state = 1;
    sleep(1);
    times = 100;
    while(times) {
        pthread_mutex_lock(&g_mutex);
        if(g_data.top_half_data != g_data.bottom_half_data) {
            result = RET_FAILED;
        }
        pthread_mutex_unlock(&g_mutex);
        
        if (result == RET_FAILED) {
            break;
        }        
    
        times--;    
    }
    
    g_state = 2;
    
    //wait threads complete
    for (i = 0; i < cpu_cnt - 1; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("pthread_join");
            break;
        }
    }
     
    if (!result) {
        printf("data protected by mutex lock\n");
    } else {
        printf("data not protected by mutex lock\n");
    }
   
    return result;
}
